<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>回调函数和异步机制 | Brilliance02</title><meta name="keywords" content="JS"><meta name="author" content="张朝辉"><meta name="copyright" content="张朝辉"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="JS异步执行机制">
<meta property="og:type" content="article">
<meta property="og:title" content="回调函数和异步机制">
<meta property="og:url" content="http://brilliance02.gitee.io/2022/06/07/article/index.html">
<meta property="og:site_name" content="Brilliance02">
<meta property="og:description" content="JS异步执行机制">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://brilliance02.gitee.io/img/6.jpg">
<meta property="article:published_time" content="2022-06-07T01:39:35.000Z">
<meta property="article:modified_time" content="2022-06-07T02:51:21.775Z">
<meta property="article:author" content="张朝辉">
<meta property="article:tag" content="JS">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://brilliance02.gitee.io/img/6.jpg"><link rel="shortcut icon" href="/img/favicon.png"><link rel="canonical" href="http://brilliance02.gitee.io/2022/06/07/article/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '回调函数和异步机制',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-06-07 10:51:21'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.2.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/img/1.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">4</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">3</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">2</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 链接</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/music/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/comment/"><i class="fa-fw fa fa-paper-plane"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('/img/6.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">Brilliance02</a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 链接</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/music/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/comment/"><i class="fa-fw fa fa-paper-plane"></i><span> 留言板</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">回调函数和异步机制</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-06-07T01:39:35.000Z" title="发表于 2022-06-07 09:39:35">2022-06-07</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-06-07T02:51:21.775Z" title="更新于 2022-06-07 10:51:21">2022-06-07</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/JS%E6%8F%90%E9%AB%98/">JS提高</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">7.2k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>24分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="回调函数和异步机制"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="JS执行机制"><a href="#JS执行机制" class="headerlink" title="JS执行机制"></a>JS执行机制</h1><h2 id="进程和线程"><a href="#进程和线程" class="headerlink" title="进程和线程"></a>进程和线程</h2><h4 id="含义"><a href="#含义" class="headerlink" title="含义"></a>含义</h4><ol>
<li><strong>进程（process）</strong>：保存在硬盘上的程序会被载入到内存中运行，在内存空间里面形成一个独立的内存体，这个内存体有自己独立的地址空间，系统也会为应用的每一个进程分配独立的CPU、内存等资源。</li>
</ol>
<ul>
<li><strong>进程就是程序的一次执行过程</strong>，程序是静态的，它作为系统中的一种资源是永远存在的。而进程是动态的，它是动态的产生，变化和消亡的，拥有其自己的生命周期。</li>
<li><strong>操作系统进行资源分配和保护的基本单位</strong>。</li>
<li>进程不仅包含正在运行的程序实体，并且包括这个运行的程序中占据的所有系统资源</li>
</ul>
<ol start="2">
<li><strong>线程（thread）</strong>：进程中执行的每一个任务指的就是线程，系统不会为其分配内存资源，<u>各个线程共享进程拥有的内存资源</u>(线程也被称为轻量级进程)。</li>
</ol>
<ul>
<li><strong>一个进程中可以有多个线程，它们共享这个进程的资源。</strong></li>
<li><strong>线程又称为迷你进程，但是它比进程更容易创建，也更容易撤销</strong>。</li>
</ul>
<h4 id="状态"><a href="#状态" class="headerlink" title="状态"></a>状态</h4><ol>
<li><strong>进程三态</strong></li>
</ol>
<ul>
<li><p>就绪：获取CPU外的所有资源、只要CPU分配资源就可以马上执行</p>
</li>
<li><p>运行：获得处理器分配的资源，程序开始执行</p>
</li>
<li><p>阻塞：正在执行的进程由于某些事件（I&#x2F;O请求，申请缓存区失败）而暂时无法运行，进程受到阻塞，在满足请求时进入就绪状态等待系统调用</p>
</li>
</ul>
<ol start="2">
<li><strong>线程三态</strong></li>
</ol>
<ul>
<li><p>就绪：指线程具备运行的所有条件，逻辑上可以运行，在等待处理机</p>
</li>
<li><p>运行：指线程占用处理机正在运行</p>
</li>
<li><p>阻塞：线程在等待一个事件，逻辑上不可执行</p>
</li>
</ul>
<h4 id="关系"><a href="#关系" class="headerlink" title="关系"></a>关系</h4><ul>
<li><p>一个程序至少有一个进程，一个进程至少有一个线程。</p>
</li>
<li><p>线程不能够脱离进程而独立运行，同一进程的所有线程共享该进程的所有资源；</p>
</li>
<li><p>进程是cpu资源<strong>分配</strong>的最小的单位，线程是cpu资源<strong>调度</strong>的最小的单位</p>
</li>
<li><p>当进程运行时只产生一个线程，被称为单线程，否则被称为多线程。</p>
</li>
</ul>
<h2 id="并发与并行"><a href="#并发与并行" class="headerlink" title="并发与并行"></a>并发与并行</h2><p><strong>并发</strong>：在操作系统中，某一时间段，<strong>几个程序在同一个CPU上运行，但在任意一个时间点上，只有一个程序在CPU上运行</strong>。 并发解决了程序排队等待的问题，如果一个程序发生阻塞，其他程序仍然可以正常执行。 </p>
<p><strong>并行：</strong>当操作系统有<strong>多个CPU时，一个CPU处理A线程，另一个CPU处理B线程</strong>，两个线程互相不抢占CPU资源，可以<strong>同时进行</strong>，这种方式成为并行。</p>
<p><strong>区别：是否同时</strong></p>
<ol>
<li>并发只是在宏观上给人感觉有多个程序在同时运行，但在实际的单CPU系统中，<strong>每一时刻只有一个程序在运行，微观上这些程序是分时交替执行</strong>。</li>
<li>在多CPU系统中，将这些<strong>并发执行的程序分配到不同的CPU上处理</strong>，每个CPU用来处理一个程序，这样多个程序便可以实现同时执行。</li>
<li>知乎上高赞例子：</li>
</ol>
<ul>
<li><p>你吃饭吃到一半，电话来了，你一直到吃完了以后才去接，这就说明你不支持并发也不支持并行。</p>
</li>
<li><p>你吃饭吃到一半，电话来了，你停了下来接了电话，接完后继续吃饭，这说明你支持并发。</p>
</li>
<li><p>你吃饭吃到一半，电话来了，你一边打电话一边吃饭，这说明你支持并行。</p>
</li>
</ul>
<h2 id="JavaScript-协程"><a href="#JavaScript-协程" class="headerlink" title="JavaScript 协程"></a>JavaScript 协程</h2><p><strong>协程定义</strong>：一种<strong>用户态的轻量级线程</strong>，协程的调度完全由用户控制（进程和线程都是由cpu 内核进行调度,或者可以理解为<strong>协程就是线程中可以交替运行的代码片段</strong>。</p>
<p><strong>与线程的比较</strong></p>
<ul>
<li>线程是为了解决阻塞和并发的问题（在一段时间内执行更多的程序），协程是为了在一段时间运行更多的“程序”（应该说是函数）并且避免线程阻塞。</li>
<li>线程切换是由操作系统的<strong>时间片控制,<strong>而协程是程序自己实现的，让协程不断轮流执行才是实现并发，所以实现协程还必须要有一个类似于时间片的结构，不同于线程的切换，</strong>协程的切换</strong>不是按照时间来算的，而是<strong>按照代码既定分配</strong>，就是说代码运行到这一行才启动协程，协程是可以由我们程序员自己操控的。</li>
</ul>
<h1 id="JavaScript异步机制"><a href="#JavaScript异步机制" class="headerlink" title="JavaScript异步机制"></a>JavaScript异步机制</h1><h2 id="同步与异步"><a href="#同步与异步" class="headerlink" title="同步与异步"></a>同步与异步</h2><h3 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h3><p>如果在函数返回的时候，调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果)，那么这个函数就是同步的。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//在函数返回时，获得了预期值，即2的平方根</span></span><br><span class="line"><span class="title class_">Math</span>.<span class="title function_">sqrt</span>(<span class="number">2</span>);</span><br><span class="line"><span class="comment">//在函数返回时，获得了预期的效果，即在控制台上打印了&#x27;hello&#x27;</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;hello&#x27;</span>);</span><br></pre></td></tr></table></figure>

<p>上面两个函数是同步的。</p>
<p><strong>如果函数是同步的，即使调用函数执行的任务比较耗时，也会一直等待直到得到预期结果。</strong></p>
<h3 id="异步"><a href="#异步" class="headerlink" title="异步"></a>异步</h3><p>JavaScript是通过JS引擎线程与浏览器中其他线程交互协作实现异步。（Javascript单线程的背后有浏览器的其他线程为其完成异步服务）</p>
<p>如果在函数返回的时候，调用者还不能够得到预期结果，而是需要在将来通过一定的手段得到，那么这个函数就是异步的。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//读取文件</span></span><br><span class="line">fs.<span class="title function_">readFile</span>(<span class="string">&#x27;hello.txt&#x27;</span>, <span class="string">&#x27;utf8&#x27;</span>, <span class="keyword">function</span>(<span class="params">err, data</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(data);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//网络请求</span></span><br><span class="line"><span class="keyword">var</span> xhr = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span><br><span class="line">xhr.<span class="property">onreadystatechange</span> = xxx; <span class="comment">// 添加回调函数</span></span><br><span class="line">xhr.<span class="title function_">open</span>(<span class="string">&#x27;GET&#x27;</span>, url);</span><br><span class="line">xhr.<span class="title function_">send</span>(); <span class="comment">// 发起函数</span></span><br></pre></td></tr></table></figure>

<p>上述示例中读取文件函数 <code>readFile</code>和网络请求的发起函数  <code>send</code>都将执行耗时操作，虽然函数会立即返回，但是<u>不能立刻获取预期的结果</u>，因为耗时操作交给其他线程执行，暂时获取不到预期结果（后面介绍）。而在JavaScript中通过回调函数 <code>function(err, data) &#123; console.log(data); &#125;</code>和 <code>onreadystatechange</code>   ，在耗时操作执行完成后把相应的结果信息传递给回调函数，通知执行JavaScript代码的线程执行回调。</p>
<p><strong>如果函数是异步的，发出调用之后，马上返回，但是不会马上返回预期结果。调用者不必主动等待，当被调用者得到结果之后会通过回调函数主动通知调用者。</strong></p>
<blockquote>
<ol>
<li>首先判断js代码是同步还是异步,同步就进入主线程,异步就进入event table<strong>（回调函数对应表）</strong>（用来存储 JavaScript 中的异步事件 (request, setTimeout, IO等) 及其对应的回调函数的列表）</li>
<li>异步任务在event table中注册函数,当<strong>满足触发条件</strong>后,被推入event queue</li>
<li>同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主进程中</li>
</ol>
</blockquote>
<p><strong>以AJAX请求为例，来看同步和异步的区别：</strong></p>
<ul>
<li>异步AJAX：</li>
</ul>
<blockquote>
<ul>
<li><strong>主线程：</strong>“你好，AJAX线程。请你帮我发个HTTP请求吧，我把请求地址和参数都给你了。”</li>
<li><strong>AJAX线程：</strong>“好的，主线程。我马上去发，但可能要花点儿时间呢，你可以先去忙别的。”</li>
<li><strong>主线程：</strong>：“谢谢，你拿到响应后告诉我一声啊。”</li>
<li>(接着，主线程做其他事情去了。一顿饭的时间后，它收到了响应到达的通知。)</li>
</ul>
</blockquote>
<ul>
<li>同步AJAX：</li>
</ul>
<blockquote>
<ul>
<li><strong>主线程：</strong>“你好，AJAX线程。请你帮我发个HTTP请求吧，我把请求地址和参数都给你了。”</li>
<li><strong>AJAX线程：</strong>“……”</li>
<li><strong>主线程：</strong>：“喂，AJAX线程，你怎么不说话？”</li>
<li><strong>AJAX线程：</strong>“……”</li>
<li><strong>主线程：</strong>：“喂！喂喂喂！”</li>
<li><strong>AJAX线程：</strong>“……”</li>
<li>(一炷香的时间后)</li>
<li><strong>主线程：</strong>：“喂！求你说句话吧！”</li>
<li><strong>AJAX线程：</strong>“主线程，不好意思，我在工作的时候不能说话。你的请求已经发完了，拿到响应数据了，给你。”</li>
</ul>
</blockquote>
<p>由于JavaScript是<strong>单线程</strong>的，而<strong>异步容易实现非阻塞</strong>，所以在JavaScript中对于耗时的操作或者时间不确定的操作，使用异步就成了必然的选择。</p>
<p><strong>异步过程的构成要素</strong></p>
<blockquote>
<p>“JavaScript是单线程的”。所谓单线程，是在JS引擎中负责解释和执行JavaScript代码的线程只有一个。不妨叫它<strong>主线程</strong>。</p>
<p>但实际上还存在其他线程。例如：处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程(例如在Node.js中)等等。这些线程可能存在于JS引擎之内，也可能存在于JS引擎之外，在此我们不做区分。不妨叫它们<strong>工作线程</strong>。</p>
</blockquote>
<p><code>异步函数</code>实际上很快就调用完成了。但是后面还有工作线程执行异步任务、通知主线程、主线程调用回调函数等很多步骤。我们把整个过程叫做<code>异步过程</code>。异步函数的调用在整个异步过程中，只是一小部分。</p>
<p><strong>一个异步过程通常是这样的：</strong></p>
<ol>
<li>主线程<strong>发起一个异步请求</strong>，</li>
<li>相应的工作线程<strong>接收请求</strong>并告知主线程已收到(<strong>异步函数返回</strong>)；</li>
<li>主线程可以<strong>继续执行后面的代码</strong>，同时工作线程<strong>执行异步任务</strong>；</li>
<li>工作线程完成工作后，<strong>通知主线程</strong>；</li>
<li>主线程收到通知后，执行一定的动作(调用回调函数)。</li>
</ol>
<p><strong>异步函数通常具有以下的形式：</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">A</span>(args..., callbackFn)</span><br></pre></td></tr></table></figure>

<p>它可以叫做<strong>异步过程的发起函数</strong>，或者叫做<strong>异步任务注册函数</strong>。<code>args</code>是这个函数需要的参数。<code>callbackFn</code>也是这个函数的参数，但是它比较特殊所以单独列出来。</p>
<p><strong>从主线程的角度看，一个异步过程包括下面两个要素：</strong></p>
<ul>
<li>发起函数(或叫注册函数)</li>
<li>回调函数<code>callbackFn</code></li>
</ul>
<p>它们都是在主线程上调用的，其中注册函数用来发起异步过程，回调函数用来处理结果。（发起函数和回调函数可以是分离的，如：ajax发送数据）</p>
<h3 id="JS中常见的异步"><a href="#JS中常见的异步" class="headerlink" title="JS中常见的异步"></a>JS中常见的异步</h3><h4 id="A-回调函数"><a href="#A-回调函数" class="headerlink" title="A. 回调函数"></a>A. 回调函数</h4><ol>
<li><strong>定义</strong>:你自己定义的函数、你没有调用、最终执行了</li>
<li><strong>分类:</strong> 同步和异步回调</li>
</ol>
<ul>
<li>同步回调:立即执行,完全执行完了才结束,不会放入回调队列中（高阶函数）</li>
<li>异步回调:不会立即执行,会放入回调队列将来执行</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//同步回调 (数组遍历相关回调函数)</span></span><br><span class="line"><span class="keyword">let</span> arr=[<span class="string">&#x27;s&#x27;</span>,<span class="string">&#x27;i&#x27;</span>,<span class="string">&#x27;p&#x27;</span>,<span class="string">&#x27;c&#x27;</span>]</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)</span><br><span class="line">arr.<span class="title function_">forEach</span>(<span class="function">(<span class="params">v,k</span>)=&gt;</span>&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(v)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//异步回调  ajax请求/promise的失败成功/setTimout/setInterval</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;setTime之前&quot;</span>);</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;timeout callback&quot;</span>);</span><br><span class="line">   &#125;, <span class="number">0</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;setTime之后&quot;</span>);</span><br></pre></td></tr></table></figure>

<ol start="3">
<li><strong>回调函数产生的问题</strong></li>
</ol>
<p><strong>回调地狱</strong></p>
<p>在使用JavaScript时，为了实现某些逻辑经常会写出层层嵌套的回调函数，如果嵌套过多，会极大影响代码可读性和逻辑。（eg：A函数的参数是B函数，B函数的参数C函数……）</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> sayhello = <span class="keyword">function</span> (<span class="params">name, callback</span>) &#123;</span><br><span class="line">  <span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(name);</span><br><span class="line">    <span class="title function_">callback</span>();</span><br><span class="line">  &#125;, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">sayhello</span>(<span class="string">&quot;first&quot;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">  <span class="title function_">sayhello</span>(<span class="string">&quot;second&quot;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">    <span class="title function_">sayhello</span>(<span class="string">&quot;third&quot;</span>, <span class="keyword">function</span> (<span class="params"></span>) &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;end&quot;</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><strong>控制反转(IOC)</strong> </p>
<p>回调最大的问题,会导致信任链断裂,使用回调需要应用某种逻辑解决所有这些控制反转导致的信任问题.与<strong>大脑方式不同</strong>有顺序性问题   <strong>控制反转造成信任问题</strong></p>
<h4 id="B-事件监听"><a href="#B-事件监听" class="headerlink" title="B. 事件监听"></a>B. 事件监听</h4><p>任务的执行不取决于代码的顺序，而取决于某个事件是否发生</p>
<h4 id="C-发布订阅模式"><a href="#C-发布订阅模式" class="headerlink" title="C.发布订阅模式"></a>C.发布订阅模式</h4><h4 id="D-Promise对象"><a href="#D-Promise对象" class="headerlink" title="D. Promise对象"></a>D. Promise对象</h4><h5 id="是什么"><a href="#是什么" class="headerlink" title="是什么"></a>是什么</h5><p>promise就是一种异步编程模式</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> <span class="comment">//调用了Promise构造函数&#123;</span></span><br><span class="line">  <span class="comment">// 做一些事情</span></span><br><span class="line">  <span class="comment">// 然后在某些条件下resolve，或者reject</span></span><br><span class="line">  <span class="keyword">if</span> (<span class="comment">/* 条件随便写^_^ */</span>) &#123;</span><br><span class="line">    <span class="title function_">resolve</span>()</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="title function_">reject</span>()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">p.<span class="title function_">then</span>(<span class="function">() =&gt;</span> &#123; <span class="comment">//调用了promise实例的.then方法</span></span><br><span class="line">    <span class="comment">// 如果p的状态被resolve了，就进入这里</span></span><br><span class="line">&#125;, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 如果p的状态被reject</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>第一段调用了Promise构造函数，第二段是调用了promise实例的.then方法</p>
<ol>
<li>构造实例</li>
</ol>
<ul>
<li>构造函数接受<strong>一个函数作为参数</strong></li>
<li>调用构造函数得到实例p的同时，<strong>作为参数的函数会立即执行</strong></li>
<li>参数函数接受<strong>两个回调函数参数</strong>resolve和reject</li>
<li>在参数函数被执行的过程中，如果在其内部调用<strong>resolve</strong>，会将p的状态变成<strong>fulfilled</strong>，或者调用<strong>reject</strong>，会将p的状态变成<strong>rejected</strong></li>
</ul>
<ol start="2">
<li>调用.then</li>
</ol>
<ul>
<li>调用.then可以<strong>为实例p注册两种状态回调函数</strong></li>
<li>当实例p的状态为<strong>fulfilled</strong>，会触发<strong>第一个</strong>函数执行</li>
<li>当实例p的状态为<strong>rejected</strong>，则触发<strong>第二个</strong>函数执行</li>
</ul>
<blockquote>
<p>有多个异步任务，且任务间有依赖关系（一个任务需要拿到另一个任务成功后的结果才能开始执行）的时候回调函数会产生回调地狱问题。</p>
</blockquote>
<ol>
<li>Promise是一个构造函数,用来封装一个异步操作并可以获得异步执行结果</li>
<li>Promise的参数是一个<strong>回调函数(同步)</strong>,返回值为仍Promise对象</li>
<li>Promise对象必须实现then方法，而且then必须返回一个Promise对象，<strong>同一个Promise对象的then可以调用多次</strong>，并且<u>回调的执行顺序跟它们被定义时的顺序一致</u></li>
<li><strong>then</strong>方法接受两个参数，第一个参数是<strong>成功时的回调</strong>(异步)，在promise由“等待”态转换到“完成”态时调用.另一个是<strong>失败时的回调</strong>(异步).</li>
<li>then方法返回的是一个新的Promise实例（<strong>注意，不是原来那个Promise实例</strong>）。因此可以采用链式写法，即then方法后面再调用另一个then方法。</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*如果第一个then 不返回value，第二个then获取不到value。*/</span></span><br><span class="line"><span class="keyword">const</span> p1=<span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123;</span><br><span class="line">    <span class="title function_">resolve</span>(<span class="string">&quot;1&quot;</span>)</span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="function"><span class="params">val</span>=&gt;</span>&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(val+<span class="string">&#x27;2&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> val;</span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="function"><span class="params">val</span>=&gt;</span>&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(val+<span class="string">&#x27;3&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="comment">//12</span></span><br><span class="line"><span class="comment">//13</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*无法访问*/</span></span><br><span class="line"><span class="keyword">const</span> p1=<span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123;</span><br><span class="line">    <span class="title function_">resolve</span>(<span class="string">&quot;1&quot;</span>)</span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="function"><span class="params">val</span>=&gt;</span>&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(val+<span class="string">&#x27;2&#x27;</span>)</span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="function"><span class="params">val</span>=&gt;</span>&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(val+<span class="string">&#x27;3&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">//12</span></span><br><span class="line"><span class="comment">// undefined3</span></span><br></pre></td></tr></table></figure>

<h5 id="3种状态"><a href="#3种状态" class="headerlink" title="3种状态"></a><strong>3种</strong>状态</h5><ul>
<li><p>pending（待定）</p>
</li>
<li><p>resolved&#x2F;fullfilled（已执行..已成功）</p>
</li>
<li><p>rejected（已拒绝..已失败）</p>
</li>
<li><p>不能逆向转换，同时“完成”态和“拒绝”态不能相互转换</p>
</li>
<li><p>注意:</p>
<ul>
<li>只有这两种状态转变,且一个Promise对象只能改变一次状态</li>
</ul>
</li>
<li><p>无论成功或失败都会有一个结果</p>
</li>
</ul>
<h5 id="resolve和reject函数"><a href="#resolve和reject函数" class="headerlink" title="resolve和reject函数"></a><strong>resolve和reject函数</strong></h5><p>调用resolve和reject能将分别将promise实例的状态变成fulfilled和rejected，只有状态变成已完成（即fulfilled和rejected之一），才能触发状态的回调</p>
<h5 id="Promise-API"><a href="#Promise-API" class="headerlink" title="Promise API"></a><strong>Promise API</strong></h5><p>promise的内容分为构造函数、实例方法和静态方法</p>
<ul>
<li>1个构造函数： new Promise</li>
<li>2个实例方法：.then 和 .catch</li>
<li>4个静态方法：Promise.all、Promise.race、Promise.resolve和Promise.reject</li>
</ul>
<p><strong>new Promise</strong></p>
<p>能将一个异步过程转化成promise对象。先有了promise对象，然后才有promise编程方式。</p>
<p><strong>.then（）</strong></p>
<ol>
<li><p>接收两个函数作为参数，用于<strong>为promise对象的状态注册回调函数</strong>。</p>
</li>
<li><p>.then()返回一个新的Promise实例，所以它可以链式调用，也就是.then后面可以继续.then</p>
</li>
<li><p>当前面的Promise状态改变时，.then()根据其最终状态，选择特定的状态响应函数执行</p>
</li>
<li><p>状态响应函数可以返回新的promise，或其他值；</p>
</li>
<li><p>如果返回新的promise，那么下一级.then()会在新的promise状态改变之后执行</p>
</li>
<li><p>如果返回其他任何值，则会立即执行下一级.then()</p>
</li>
<li><p>在注册的<strong>状态回调函数</strong>中，可以通过return语句<u>改变.then返回的promise对象的状态</u>，以及向后面.then注册的状态回调<u>传递数据</u>；也可以不使用return语句，那样<u>默认就是将返回的promise对象resolve</u>。</p>
</li>
</ol>
<p><strong>.then()里面有.then()的情况</strong></p>
<p>1、因为.then()返回的还是Promise实例<br>2、会等里面的then()执行完，再执行外面的</p>
<p><img src="https://upload-images.jianshu.io/upload_images/15311104-7aa75490f79e0725.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1200/format/webp" alt="img"></p>
<p><strong>reject回调</strong></p>
<p><img src="https://upload-images.jianshu.io/upload_images/15311104-d93b7cf287478e43.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/860/format/webp" alt="img"></p>
<p><img src="https://upload-images.jianshu.io/upload_images/15311104-5e3ab96cbdcde863.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/861/format/webp" alt="img"></p>
<p><strong>catch() + then()</strong></p>
<p>catch也会返回一个promise实例，并且是resolved状态</p>
<p><img src="https://upload-images.jianshu.io/upload_images/15311104-f63f673f5fbb74d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/917/format/webp" alt="img"></p>
<p><img src="https://upload-images.jianshu.io/upload_images/15311104-947df4320e99263b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/417/format/webp" alt="img"></p>
<p><img src="https://upload-images.jianshu.io/upload_images/15311104-1aa27776f33dc101.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1200/format/webp" alt="img"></p>
<p><img src="https://upload-images.jianshu.io/upload_images/15311104-a1d408c57330e14b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/460/format/webp" alt="img"></p>
<p>抛出错误变为rejected状态，所以绕过两个then直接跑到最下面的catch</p>
<p><strong>.catch()</strong></p>
<p>用于注册<strong>rejected</strong>状态的回调函数，同时该回调也是<strong>程序出错的回调</strong>，即如果前面的程序运行过程中出错，也会进入执行该回调函数。同.then一样，也会返回新的promise对象。</p>
<p><strong>Promise.resolve</strong>()</p>
<p>会返回一个状态为fulfilled状态的promise对象，参数会作为数据传递给后面的状态回调函数</p>
<p>Promise.reject与Promise.resolve同理，区别在于返回的promise对象状态为rejected</p>
<p><strong>Promise.all() 批量执行</strong></p>
<ul>
<li>Promise.all([p1, p2, p3])用于<strong>将多个promise实例，包装成一个新的Promise实例</strong>，返回的实例就是普通的promise</li>
<li>它接收一个<strong>数组</strong>作为参数</li>
<li>数组里可以是Promise对象，也可以是别的值，只有Promise会等待状态改变</li>
<li>当所有的子Promise都完成，该Promise完成，返回值是全部值得数组</li>
<li>有任何一个失败，该Promise失败，返回值是第一个失败的子Promise结果</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//切菜</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">cutUp</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;开始切菜。&#x27;</span>);</span><br><span class="line">    <span class="keyword">var</span> p = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>)&#123;        <span class="comment">//做一些异步操作</span></span><br><span class="line">        <span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;切菜完毕！&#x27;</span>);</span><br><span class="line">            <span class="title function_">resolve</span>(<span class="string">&#x27;切好的菜&#x27;</span>);</span><br><span class="line">        &#125;, <span class="number">1000</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> p;<span class="comment">//p是一个promise</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//烧水</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">boil</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;开始烧水。&#x27;</span>);</span><br><span class="line">    <span class="keyword">var</span> p = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve, reject</span>)&#123;        <span class="comment">//做一些异步操作</span></span><br><span class="line">        <span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;烧水完毕！&#x27;</span>);</span><br><span class="line">            <span class="title function_">resolve</span>(<span class="string">&#x27;烧好的水&#x27;</span>);</span><br><span class="line">        &#125;, <span class="number">1000</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">all</span>([<span class="title function_">cutUp</span>(), <span class="title function_">boil</span>()])<span class="comment">//传入数组</span></span><br><span class="line">    .<span class="title function_">then</span>(<span class="function">(<span class="params">result</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;准备工作完毕&#x27;</span>);</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(result);</span><br><span class="line">    &#125;)</span><br></pre></td></tr></table></figure>

<p>![15e21baa57c6bbbb7b21702f41e7fe3](C:\Users\zzh\AppData\Local\Temp\WeChat Files\15e21baa57c6bbbb7b21702f41e7fe3.png)</p>
<p><strong>Promise.race() 类似于Promise.all() ，区别在于它有任意一个完成就算完成</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> p1 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="title function_">resolve</span>(<span class="string">&#x27;I\`m p1 &#x27;</span>)</span><br><span class="line">    &#125;, <span class="number">1000</span>)</span><br><span class="line">&#125;);</span><br><span class="line"><span class="keyword">let</span> p2 = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;111&quot;</span>);</span><br><span class="line">    <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="title function_">resolve</span>(<span class="string">&#x27;I\`m p2 &#x27;</span>)</span><br><span class="line">    &#125;, <span class="number">2000</span>)</span><br><span class="line">&#125;);</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">race</span>([p1, p2])</span><br><span class="line">    .<span class="title function_">then</span>(<span class="function"><span class="params">value</span> =&gt;</span> &#123;		<span class="comment">//p1完成立即调用then</span></span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(value)</span><br><span class="line">    &#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">//先输出111，然后输出I`m p1 ，&#x27;I\`m p2不输出</span></span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//promise简单举例</span></span><br><span class="line"><span class="keyword">const</span> p = <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;start promise&quot;</span>);</span><br><span class="line">  <span class="keyword">let</span> a=<span class="number">1</span></span><br><span class="line">  <span class="keyword">if</span>(a==<span class="number">1</span>)&#123;</span><br><span class="line">  <span class="title function_">resolve</span>(<span class="string">&quot;success&quot;</span>);</span><br><span class="line">  &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">  <span class="title function_">reject</span>(<span class="string">&quot;error&quot;</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line">p.<span class="title function_">then</span>(<span class="function">(<span class="params">val</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(val);<span class="comment">//&quot;success&quot;</span></span><br><span class="line">&#125;);</span><br><span class="line">p.<span class="title function_">catch</span>(<span class="function"><span class="params">err</span>=&gt;</span>&#123;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(err)</span><br><span class="line">&#125;)</span><br><span class="line">p.<span class="title function_">finally</span>(<span class="function">()=&gt;</span>&#123; <span class="comment">//p执行完毕，调用</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">123</span>)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;sync task&quot;</span>);</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">start promise</span></span><br><span class="line"><span class="comment">sync task</span></span><br><span class="line"><span class="comment">success</span></span><br><span class="line"><span class="comment">123</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//进阶(Assignment)</span></span><br><span class="line"><span class="comment">//eg1</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;sync task1&quot;</span>);</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve,reject</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">123</span>)</span><br><span class="line">        <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">          <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;setTimeout&#x27;</span>)</span><br><span class="line">          <span class="title function_">resolve</span>(<span class="string">&quot;成功的数据&quot;</span>);</span><br><span class="line">          <span class="title function_">reject</span>(<span class="string">&quot;失败的数据&quot;</span>);</span><br><span class="line">        &#125;, <span class="number">1000</span>);</span><br><span class="line">    &#125;).<span class="title function_">then</span>(</span><br><span class="line">    <span class="function"><span class="params">value</span>=&gt;</span><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;onResolved()1&quot;</span>, value)</span><br><span class="line">    )</span><br><span class="line">    .<span class="title function_">catch</span>(</span><br><span class="line">    <span class="function"><span class="params">reason</span>=&gt;</span><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;onRejected()1&quot;</span>, reason)</span><br><span class="line">    )</span><br><span class="line">   <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;sync task2&quot;</span>);</span><br><span class="line"><span class="comment">//sync task1  123  sync task2  setTimeout onResolved()1成功的数据</span></span><br><span class="line">   </span><br><span class="line"><span class="comment">//eg2</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;timout callback()&quot;</span>);</span><br><span class="line">      <span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">1</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">value</span>) =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;onResolved&quot;</span>, value));</span><br><span class="line">   &#125;, <span class="number">0</span>);</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;timout callback2()&quot;</span>);</span><br><span class="line">    &#125;, <span class="number">0</span>);</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">2</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">value</span>) =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;onResolved&quot;</span>, value));</span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>(<span class="number">3</span>).<span class="title function_">then</span>(<span class="function">(<span class="params">value</span>) =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;onResolved2&quot;</span>, value));</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//onResolved 2</span></span><br><span class="line"><span class="comment">//onResolved2  3</span></span><br><span class="line"><span class="comment">//timout callback()</span></span><br><span class="line"><span class="comment">//onResolved 1</span></span><br><span class="line"><span class="comment">//timout callback2()</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//eg3</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>);</span><br><span class="line"> &#125;, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>);</span><br><span class="line">      <span class="title function_">resolve</span>();</span><br><span class="line">    &#125;)</span><br><span class="line">      .<span class="title function_">then</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">3</span>);</span><br><span class="line">      &#125;) </span><br><span class="line">      .<span class="title function_">then</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">4</span>);</span><br><span class="line">      &#125;);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">5</span>);</span><br><span class="line"><span class="comment">//2 5 3 4 1</span></span><br></pre></td></tr></table></figure>

<h4 id="E-Generator-函数"><a href="#E-Generator-函数" class="headerlink" title="E. Generator 函数"></a>E. Generator 函数</h4><h4 id="F-async-x2F-await"><a href="#F-async-x2F-await" class="headerlink" title="F.async&#x2F;await"></a>F.async&#x2F;await</h4><ol>
<li>async是用来声明一个异步方法,await用来等待异步方法的执行</li>
<li>正常情况下<code>await</code>后边是一个Promise对象,返回该对象的结果。如果不是Promise对象,直接返回封装好的promise。<code>await</code><strong>都会阻塞函数中<u>后边</u>的代码（即加入<u>微队列</u>）</strong></li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//eg1</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">f</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> <span class="number">123</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_">f</span>().<span class="title function_">then</span>(<span class="function"><span class="params">value</span>=&gt;</span><span class="variable language_">console</span>.<span class="title function_">log</span>(value))<span class="comment">//123</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//eg2</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">fn1</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">await</span> <span class="title function_">fn2</span>();</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>);<span class="comment">//阻塞</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">fn2</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;fn2&#x27;</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">fn1</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">3</span>)</span><br><span class="line"><span class="comment">//1 fn2 3 2</span></span><br></pre></td></tr></table></figure>

<h2 id="单线程与多线程"><a href="#单线程与多线程" class="headerlink" title="单线程与多线程"></a>单线程与多线程</h2><p>JavaScript是单线程，怎么还存在异步？？</p>
<h3 id="浏览器"><a href="#浏览器" class="headerlink" title="浏览器"></a>浏览器</h3><p>目前最为流行的浏览器为：Chrome，IE，Safari，FireFox，Opera。<strong>浏览器的内核是多线程的</strong>。</p>
<p>一个浏览器通常由以下几个常驻的线程：</p>
<ul>
<li><strong>渲染引擎线程</strong>：顾名思义，该线程负责页面的渲染</li>
<li><strong>JS引擎线程</strong>：负责JS的解析和执行</li>
<li><strong>定时触发器线程</strong>：处理定时事件，比如setTimeout, setInterval</li>
<li><strong>事件触发线程</strong>：处理DOM事件</li>
<li><strong>异步http请求线程</strong>：处理http请求</li>
</ul>
<p>注意，<strong>渲染线程和JS引擎线程是不能同时进行</strong>。渲染线程在执行任务的时候，JS引擎线程会被挂起。因为JS可以操作DOM，若在渲染中JS处理了DOM，浏览器可能就不知所措</p>
<h3 id="JS引擎"><a href="#JS引擎" class="headerlink" title="JS引擎"></a>JS引擎</h3><p>通常讲到浏览器的时候，我们会说到两个引擎：渲染引擎和JS引擎。</p>
<p><strong>渲染引擎就是如何渲染页面</strong>。不同的引擎对同一个样式的实现不一致，导致了经常被人诟病的浏览器样式兼容性问题。</p>
<p><strong>JS引擎负责JS代码的解析和执行</strong>。通常包括以下几个步骤：</p>
<ul>
<li>词法分析：将源代码分解为有意义的分词</li>
<li>语法分析：用语法分析器将分词解析成语法树</li>
<li>代码生成：生成机器能运行的代码</li>
<li>代码执行</li>
</ul>
<p>不同浏览器的JS引擎也各不相同。</p>
<p>之所以说JavaScript是单线程，是因为<strong>JS引擎中负责解释和执行JavaScript代码的线程只有一个</strong></p>
<p>为什么只有一个引擎呢？</p>
<p>如果同时有两个线程去操作DOM，浏览器是不是又要不知所措了。</p>
<p><strong>虽然JavaScript是单线程的，可是浏览器内部不是单线程的。一些I&#x2F;O操作、定时器的计时和事件监听（click, keydown…）等都是由浏览器提供的其他线程来完成的。</strong></p>
<h2 id="消息队列与事件循环"><a href="#消息队列与事件循环" class="headerlink" title="消息队列与事件循环"></a>消息队列与事件循环</h2><blockquote>
<p>工作线程将消息放到消息队列，主线程通过事件循环过程去取消息。</p>
</blockquote>
<ul>
<li><p><strong>消息队列</strong>：消息队列是一个<strong>先进先出</strong>的队列，它里面存放着各种消息。</p>
</li>
<li><p><strong>事件循环</strong>：事件循环是指主线程重复从消息队列中取消息、执行的过程。</p>
</li>
<li><p><strong>消息</strong>就是注册异步任务时添加的回调函数。</p>
</li>
</ul>
<p><img src="https://upload-images.jianshu.io/upload_images/3985563-b1682e665abed777.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/596/format/webp" alt="img"></p>
<ul>
<li>左边的栈存储的是同步任务，就是那些能立即执行、不耗时的任务，如变量和函数的初始化、事件的绑定等等那些不需要回调函数的操作都可归为这一类。</li>
<li>右边的堆用来存储声明的变量、对象。</li>
<li>下面的队列就是消息队列，一旦某个<strong>异步任务有了响应消息就会被推入队列中</strong>。如用户的点击事件、浏览器收到服务的响应和setTimeout中待执行的事件。</li>
</ul>
<h3 id="执行过程："><a href="#执行过程：" class="headerlink" title="执行过程："></a><strong>执行过程：</strong></h3><ol>
<li><strong>JS引擎线程用来执行栈中的同步任务</strong>，当<strong>所有同步任务执行完毕后，栈被清空</strong>，</li>
<li>然后<strong>读取消息队列中的一个待处理任务</strong>，并<strong>把相关回调函数压入栈中</strong>，<strong>单线程开始执行新的同步任务</strong>。</li>
</ol>
<p>JS引擎线程从消息队列中读取任务是<strong>不断循环的</strong>，<strong>每次栈被清空后，都会在消息队列中读取新的任务</strong>，如果<strong>没有新的任务，就会等待，直到有新的任务</strong>，这就叫<strong>事件循环</strong>。</p>
<p>可以得到一个明显的结论，就是：</p>
<blockquote>
<p>异步过程的回调函数，一定不在当前这一轮事件循环中执行。</p>
</blockquote>
<h3 id="案例："><a href="#案例：" class="headerlink" title="案例："></a>案例：</h3><p><code>setTimeout(fn, 0)</code>的意思是，将回调函数fn<strong>立刻插入</strong>消息队列，<strong>等待执行</strong>，而不是立即执行。一个例子：</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;a&quot;</span>)</span><br><span class="line">&#125;, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> i=<span class="number">0</span>; i&lt;<span class="number">10000</span>; i++) &#123;&#125;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;b&quot;</span>)</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">b  a</span><br><span class="line"><span class="comment">//打印结果表明回调函数并没有立刻执行，而是等待栈中的任务执行完毕后才执行的。栈中的任务执行多久，它就得等多久。</span></span><br></pre></td></tr></table></figure>

<p>执行下面这段代码，执行后，在 5s 内点击两下，过一段时间（&gt;5s）后，再点击两下，整个过程的输出结果是什么？</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">100000000</span>; i++)&#123;&#125;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;timer a&#x27;</span>);</span><br><span class="line">&#125;, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> j = <span class="number">0</span>; j &lt; <span class="number">5</span>; j++)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(j);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;timer b&#x27;</span>);</span><br><span class="line">&#125;, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">waitFiveSeconds</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="keyword">var</span> now = (<span class="keyword">new</span> <span class="title class_">Date</span>()).<span class="title function_">getTime</span>();<span class="comment">//获取的是毫秒</span></span><br><span class="line">    <span class="keyword">while</span>(((<span class="keyword">new</span> <span class="title class_">Date</span>()).<span class="title function_">getTime</span>() - now) &lt; <span class="number">5000</span>)&#123; &#125;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;finished waiting&#x27;</span>);<span class="comment">//</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;click&#x27;</span>, <span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;click&#x27;</span>);</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;click begin&#x27;</span>);</span><br><span class="line"><span class="title function_">waitFiveSeconds</span>();</span><br><span class="line"><span class="comment">// 01234  click begin  finished waiting  timer a  timer b</span></span><br></pre></td></tr></table></figure>

<p>要想了解上述代码的输出结果，首先介绍下定时器。</p>
<p><code>setTimeout</code>的作用是在间隔一定的时间后，将回调函数插入消息队列中，等栈中的同步任务都执行完毕后，再执行。</p>
<p><code>setTimeout(fn, 0)</code>的意思是，将回调函数fn<strong>立刻插入</strong>消息队列，<strong>等待执行</strong>，而不是立即执行。</p>
<ol>
<li>首先，先执行同步任务。其中<code>waitFiveSeconds</code>是耗时操作，持续执行长达5s。</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0</span></span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">4</span></span><br><span class="line">click begin</span><br><span class="line"><span class="comment">//5s后输出</span></span><br><span class="line">finished waiting</span><br></pre></td></tr></table></figure>

<ol start="2">
<li>然后，在JS引擎线程执行的时候，’timer a’对应的定时器产生的回调、 ‘timer b’对应的定时器产生的回调和两次 click 对应的回调被先后放入消息队列。JS引擎线程空闲后，会<strong>先查看是否有事件可执行</strong>，接着再处理其他异步任务。</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">click</span><br><span class="line">click</span><br><span class="line">timer a</span><br><span class="line">timer b</span><br></pre></td></tr></table></figure>

<p>最后，5s 后的两次 click 事件被放入消息队列，由于此时JS引擎线程空闲，便被立即执行了。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">click</span><br><span class="line">click</span><br></pre></td></tr></table></figure>

<h2 id="宏任务和微任务"><a href="#宏任务和微任务" class="headerlink" title="宏任务和微任务"></a>宏任务和微任务</h2><p><strong>异步任务</strong>又包括<strong>宏任务（macrotask ）和微任务（microtask ）</strong></p>
<h3 id="是什么-1"><a href="#是什么-1" class="headerlink" title="是什么"></a>是什么</h3><table>
<thead>
<tr>
<th></th>
<th><strong>宏任务（macrotask）</strong></th>
<th><strong>微任务（microtask）</strong></th>
</tr>
</thead>
<tbody><tr>
<td>谁发起的</td>
<td>宿主（Node、浏览器）</td>
<td>JS引擎</td>
</tr>
<tr>
<td>具体事件</td>
<td></td>
<td></td>
</tr>
<tr>
<td>谁先运行</td>
<td>后运行</td>
<td>先运行</td>
</tr>
<tr>
<td>会触发新一轮Tick吗</td>
<td>会</td>
<td>不会</td>
</tr>
</tbody></table>
<blockquote>
<p>每一个宏任务都带着一个专属于自己的微任务队列！！！</p>
<p>会在当前所属的宏任务结束的时候去执行它微任务队列里面的东西。</p>
</blockquote>
<h3 id="有哪些"><a href="#有哪些" class="headerlink" title="有哪些"></a>有哪些</h3><p>宏任务：</p>
<ol>
<li>script(整体代码)</li>
<li>setTimeout</li>
<li>setInterval</li>
<li>setImmediate</li>
<li>I&#x2F;O  文件读取</li>
<li>UI rendering</li>
</ol>
<p>微任务：</p>
<ul>
<li>Promise</li>
<li>DOM</li>
<li>async&#x2F;await</li>
<li>process.nextTick（Node.js）</li>
<li>Object.observe（已废弃；Proxy 对象替代）</li>
<li>MutaionObserver</li>
</ul>
<h3 id="怎么执行"><a href="#怎么执行" class="headerlink" title="怎么执行"></a>怎么执行</h3><p><strong>执行顺序：</strong></p>
<p>先执行<strong>同步代码</strong></p>
<p>遇到异步宏任务则将异步宏任务放入<strong>宏任务队列</strong>中</p>
<p>遇到异步微任务则将异步微任务放入<strong>微任务队列</strong>中</p>
<p>当所有同步代码执行完毕后，再将<strong>异步微任务</strong>从队列中调入主线程执行</p>
<p>微任务执行完毕后再将<strong>异步宏任务</strong>从队列中调入主线程执行</p>
<p>一直循环直至所有任务执行完毕。 </p>
<p>案例：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;1&#x27;</span>);</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;2&#x27;</span>);</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;3&#x27;</span>);</span><br><span class="line">        <span class="title function_">resolve</span>();</span><br><span class="line">    &#125;).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;4&#x27;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;5&#x27;</span>);</span><br><span class="line">    <span class="title function_">resolve</span>();</span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;6&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;7&#x27;</span>);</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;8&#x27;</span>);<span class="comment">//注意：在这儿没有调用resolve()</span></span><br><span class="line">    &#125;).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;9&#x27;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;10&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1 5 10 6 2 3 4 7 8</span></span><br></pre></td></tr></table></figure>

<p><strong>分析：</strong></p>
<p>第一轮：</p>
<ol>
<li>首先是进入主线程，遇到 console.log(‘1’)，打印1</li>
<li>然后遇到 promise 中的 console.log(‘5’); 直接执行，打印5</li>
<li>再遇到 console.log(‘10’)，打印10</li>
</ol>
<p>第二轮：</p>
<ol>
<li>然后运行微任务，我们看到这里面的微任务有 promise，依次执行。</li>
<li>首先promise完成态，走到 then，遇到 console.log(‘6’)，打印6</li>
</ol>
<p>第三轮：</p>
<ol>
<li>然后运行宏任务，我们看到这里面的微任务有 setTimeout，依次执行。</li>
<li>遇到 console.log(‘2’)，打印2，然后发现里面还有个微任务 promise，先打印3，等完成后，到 then，打印4</li>
<li>遇到第二个 setTimeout 打印7，然后发现里面还有个微任务 promise，先打印8，由于没有完成态resolve()，所以不打印9</li>
</ol>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="http://brilliance02.gitee.io">张朝辉</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="http://brilliance02.gitee.io/2022/06/07/article/">http://brilliance02.gitee.io/2022/06/07/article/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="http://brilliance02.gitee.io" target="_blank">Brilliance02</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/JS/">JS</a></div><div class="post_share"><div class="social-share" data-image="/img/6.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/gh/overtrue/share.js@master/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-full"><a href="/2022/06/07/this/"><img class="prev-cover" src="/img/11.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">JS的作用域和this</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2022/06/07/this/" title="JS的作用域和this"><img class="cover" src="/img/11.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-06-07</div><div class="title">JS的作用域和this</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="/img/1.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">张朝辉</div><div class="author-info__description">欢迎进入ZZH's Blog</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">4</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">3</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">2</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#JS%E6%89%A7%E8%A1%8C%E6%9C%BA%E5%88%B6"><span class="toc-number">1.</span> <span class="toc-text">JS执行机制</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B"><span class="toc-number">1.1.</span> <span class="toc-text">进程和线程</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%90%AB%E4%B9%89"><span class="toc-number">1.1.0.1.</span> <span class="toc-text">含义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%8A%B6%E6%80%81"><span class="toc-number">1.1.0.2.</span> <span class="toc-text">状态</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%B3%E7%B3%BB"><span class="toc-number">1.1.0.3.</span> <span class="toc-text">关系</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B9%B6%E5%8F%91%E4%B8%8E%E5%B9%B6%E8%A1%8C"><span class="toc-number">1.2.</span> <span class="toc-text">并发与并行</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#JavaScript-%E5%8D%8F%E7%A8%8B"><span class="toc-number">1.3.</span> <span class="toc-text">JavaScript 协程</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#JavaScript%E5%BC%82%E6%AD%A5%E6%9C%BA%E5%88%B6"><span class="toc-number">2.</span> <span class="toc-text">JavaScript异步机制</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%90%8C%E6%AD%A5%E4%B8%8E%E5%BC%82%E6%AD%A5"><span class="toc-number">2.1.</span> <span class="toc-text">同步与异步</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%8C%E6%AD%A5"><span class="toc-number">2.1.1.</span> <span class="toc-text">同步</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E6%AD%A5"><span class="toc-number">2.1.2.</span> <span class="toc-text">异步</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#JS%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84%E5%BC%82%E6%AD%A5"><span class="toc-number">2.1.3.</span> <span class="toc-text">JS中常见的异步</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#A-%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0"><span class="toc-number">2.1.3.1.</span> <span class="toc-text">A. 回调函数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#B-%E4%BA%8B%E4%BB%B6%E7%9B%91%E5%90%AC"><span class="toc-number">2.1.3.2.</span> <span class="toc-text">B. 事件监听</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#C-%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F"><span class="toc-number">2.1.3.3.</span> <span class="toc-text">C.发布订阅模式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#D-Promise%E5%AF%B9%E8%B1%A1"><span class="toc-number">2.1.3.4.</span> <span class="toc-text">D. Promise对象</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%98%AF%E4%BB%80%E4%B9%88"><span class="toc-number">2.1.3.4.1.</span> <span class="toc-text">是什么</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#3%E7%A7%8D%E7%8A%B6%E6%80%81"><span class="toc-number">2.1.3.4.2.</span> <span class="toc-text">3种状态</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#resolve%E5%92%8Creject%E5%87%BD%E6%95%B0"><span class="toc-number">2.1.3.4.3.</span> <span class="toc-text">resolve和reject函数</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Promise-API"><span class="toc-number">2.1.3.4.4.</span> <span class="toc-text">Promise API</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#E-Generator-%E5%87%BD%E6%95%B0"><span class="toc-number">2.1.3.5.</span> <span class="toc-text">E. Generator 函数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#F-async-x2F-await"><span class="toc-number">2.1.3.6.</span> <span class="toc-text">F.async&#x2F;await</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8D%95%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%A4%9A%E7%BA%BF%E7%A8%8B"><span class="toc-number">2.2.</span> <span class="toc-text">单线程与多线程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B5%8F%E8%A7%88%E5%99%A8"><span class="toc-number">2.2.1.</span> <span class="toc-text">浏览器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#JS%E5%BC%95%E6%93%8E"><span class="toc-number">2.2.2.</span> <span class="toc-text">JS引擎</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E4%B8%8E%E4%BA%8B%E4%BB%B6%E5%BE%AA%E7%8E%AF"><span class="toc-number">2.3.</span> <span class="toc-text">消息队列与事件循环</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B%EF%BC%9A"><span class="toc-number">2.3.1.</span> <span class="toc-text">执行过程：</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A1%88%E4%BE%8B%EF%BC%9A"><span class="toc-number">2.3.2.</span> <span class="toc-text">案例：</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%8F%E4%BB%BB%E5%8A%A1%E5%92%8C%E5%BE%AE%E4%BB%BB%E5%8A%A1"><span class="toc-number">2.4.</span> <span class="toc-text">宏任务和微任务</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%98%AF%E4%BB%80%E4%B9%88-1"><span class="toc-number">2.4.1.</span> <span class="toc-text">是什么</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="toc-number">2.4.2.</span> <span class="toc-text">有哪些</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%80%8E%E4%B9%88%E6%89%A7%E8%A1%8C"><span class="toc-number">2.4.3.</span> <span class="toc-text">怎么执行</span></a></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/2023/04/19/Flex%E5%BC%B9%E6%80%A7%E7%9B%92/" title="Flex弹性盒"><img src="/img/14.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Flex弹性盒"/></a><div class="content"><a class="title" href="/2023/04/19/Flex%E5%BC%B9%E6%80%A7%E7%9B%92/" title="Flex弹性盒">Flex弹性盒</a><time datetime="2023-04-19T13:08:37.586Z" title="发表于 2023-04-19 21:08:37">2023-04-19</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/06/07/reg/" title="正则表达式"><img src="/img/7.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="正则表达式"/></a><div class="content"><a class="title" href="/2022/06/07/reg/" title="正则表达式">正则表达式</a><time datetime="2022-06-07T02:34:40.000Z" title="发表于 2022-06-07 10:34:40">2022-06-07</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/06/07/this/" title="JS的作用域和this"><img src="/img/11.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="JS的作用域和this"/></a><div class="content"><a class="title" href="/2022/06/07/this/" title="JS的作用域和this">JS的作用域和this</a><time datetime="2022-06-07T02:29:50.000Z" title="发表于 2022-06-07 10:29:50">2022-06-07</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/06/07/article/" title="回调函数和异步机制"><img src="/img/6.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="回调函数和异步机制"/></a><div class="content"><a class="title" href="/2022/06/07/article/" title="回调函数和异步机制">回调函数和异步机制</a><time datetime="2022-06-07T01:39:35.000Z" title="发表于 2022-06-07 09:39:35">2022-06-07</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2022 - 2023 By 张朝辉</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text">Hi, welcome to my blog !</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.js"></script><div class="js-pjax"></div><canvas class="fireworks" mobile="false"></canvas><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/fireworks.min.js"></script><script defer="defer" id="fluttering_ribbon" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>